// (C) Szymon Piechowicz, February 2012

#include "battery.h"
#include "adc_manager.h"
#include "common.h"


#define BATTERY_METER_PORT PORTA
#define BATTERY_METER_PORT_DIR DDRA
#define BATTERY_METER_BIT PA7
#define BATTERY_METER_LED_GREEN_PORT PORTA
#define BATTERY_METER_LED_GREEN_PORT_DIR DDRA
#define BATTERY_METER_LED_GREEN_BIT PA3
#define BATTERY_METER_LED_YELLOW_PORT PORTA
#define BATTERY_METER_LED_YELLOW_PORT_DIR DDRA
#define BATTERY_METER_LED_YELLOW_BIT PA2
#define BATTERY_METER_LED_RED_PORT PORTA
#define BATTERY_METER_LED_RED_PORT_DIR DDRA
#define BATTERY_METER_LED_RED_BIT PA1

#define CHARGE_ADC_GREEN_THRESHOLD 184 // 3.6 V
#define CHARGE_ADC_YELLOW_THRESHOLD 163 // 3.2 V
#define CHARGE_ADC_MIN_CHANGE 10


static void charge_adc_callback(int8_t bit, uint8_t result);


static battery_charge_callback charge_callback;
// Callbacks are fired only when charge state changes.
static bool charge_did_report_first_result;
static enum BatteryCharge charge_last_reported_result;


void battery_init(void)
{
  PORT_OFF(BATTERY_METER_PORT, BATTERY_METER_BIT);
  PORT_OFF(BATTERY_METER_LED_GREEN_PORT, BATTERY_METER_LED_GREEN_BIT);
  PORT_DIR_OUT(BATTERY_METER_LED_GREEN_PORT_DIR, BATTERY_METER_LED_GREEN_BIT);
  PORT_OFF(BATTERY_METER_LED_YELLOW_PORT, BATTERY_METER_LED_YELLOW_BIT);
  PORT_DIR_OUT(BATTERY_METER_LED_YELLOW_PORT_DIR, BATTERY_METER_LED_YELLOW_BIT);
  PORT_OFF(BATTERY_METER_LED_RED_PORT, BATTERY_METER_LED_RED_BIT);
  PORT_DIR_OUT(BATTERY_METER_LED_RED_PORT_DIR, BATTERY_METER_LED_RED_BIT);
}

void battery_start_measuring_charge(battery_charge_callback callback)
{
  charge_callback = callback;
  charge_did_report_first_result = false;
  adc_manager_start_measuring(BATTERY_METER_BIT, charge_adc_callback,
      CHARGE_ADC_MIN_CHANGE);
}

void battery_stop_measuring_charge(void)
{
  adc_manager_stop_measuring(BATTERY_METER_BIT);
}

static void charge_adc_callback(int8_t bit, uint8_t result)
{
  enum BatteryCharge charge;
  if (result > CHARGE_ADC_GREEN_THRESHOLD)
    charge = BATTERY_CHARGE_GREEN;
  else if (result > CHARGE_ADC_YELLOW_THRESHOLD)
    charge = BATTERY_CHARGE_YELLOW;
  else
    charge = BATTERY_CHARGE_RED;
  if (!charge_did_report_first_result || charge != charge_last_reported_result)
  {
    charge_did_report_first_result = true;
    charge_last_reported_result = charge;
    if (charge_callback != NULL)
      charge_callback(charge);
    if (charge == BATTERY_CHARGE_GREEN)
      PORT_ON(BATTERY_METER_LED_GREEN_PORT, BATTERY_METER_LED_GREEN_BIT);
    else
      PORT_OFF(BATTERY_METER_LED_GREEN_PORT, BATTERY_METER_LED_GREEN_BIT);
    if (charge == BATTERY_CHARGE_YELLOW)
      PORT_ON(BATTERY_METER_LED_YELLOW_PORT, BATTERY_METER_LED_YELLOW_BIT);
    else
      PORT_OFF(BATTERY_METER_LED_YELLOW_PORT, BATTERY_METER_LED_YELLOW_BIT);
    if (charge == BATTERY_CHARGE_RED)
      PORT_ON(BATTERY_METER_LED_RED_PORT, BATTERY_METER_LED_RED_BIT);
    else
      PORT_OFF(BATTERY_METER_LED_RED_PORT, BATTERY_METER_LED_RED_BIT);
  }
}